//
// Created by root on 19-7-29.
//

#include <cstring>
#include <string>
#include <unistd.h>
#include "hook_gss.h"
#include "log_utils.h"
#include "inject_compacter.h"
#include "AndHook.h"
#include "process_utils.h"
#include "file_utils.h"
#include "sig_utils.h"
#include "sys_prop_utils.h"
#include <sys/system_properties.h>

#define TAG "GetSystemService"

static jmethodID old_method= nullptr;

#define OPERATION_NAME "getSystemService"

inline bool need_auth(JNIEnv *env, const char* c_name, const char* target,const char* exclude_process) {
    if(is_exclude_process(exclude_process))
        return true;
    if (strstr(c_name, target) != nullptr) {
        std::string operation = std::string(OPERATION_NAME);
        operation.append("(\"");
        operation.append(c_name);
        operation.append("\")");
        if (!post_auth_request(env, operation)) {
            LOGE("user denies to let process:%d use service:%s\n", getpid(), c_name);
            return false;
        }
        LOGD("user allows to let process:%d use service:%s\n", getpid(), c_name);
    }
    return true;
}

inline bool need_auth_whitelist(JNIEnv *env,const char* c_name,const char* target){
    using namespace std;
    //vector<string> whitelist_lines=read_lines(TELEPHONY_WHITELIST_PATH);
    //if(contains(whitelist_lines,get_process_name(getpid()))){
    if (strstr(c_name, target) == nullptr)
        return true;
    LOGD("before getSystemService(\"%s\")\n", c_name);
    if(is_platform_signature(env)){
        LOGD("policy allows to let process:%d use service:%s\n", getpid(), c_name);
        return true;
    }
    return need_auth(env,c_name,target, nullptr);
}

#define NEED_AUTH(c_name,target,exclude_process) if(!need_auth(env,c_name,target,exclude_process)){return nullptr;}
#define NEED_AUTH_WHITELIST(c_name,target) if(!need_auth_whitelist(env,c_name,target)){return nullptr;}

//public Object getSystemService(String name)
jobject my_get_sys_service(JNIEnv *env,jobject instance,
        jstring name) {
    const char *c_name = env->GetStringUTFChars(name, nullptr);
    if (!old_method) {
        LOGE("fail to get old_method\n");
        return nullptr;
    }
    jobject service = env->CallObjectMethod(instance, old_method,
            name);
    NEED_AUTH(c_name,"camera","com.android.systemui");// excluded
    NEED_AUTH(c_name,"location","system_server");// excluded
    if(get_phone_brand() == "Android") {//只在AOSP中限制phone服务调用（samsung任何程序启动都会调用phone服务）
        NEED_AUTH_WHITELIST(c_name, "phone");
    }
    env->ReleaseStringUTFChars(name, c_name);
    return service;
}

void hook_get_sys_service(JNIEnv *env) {
    if(old_method)
        return;
    jclass context_wrapper_class=env->FindClass("android/content/ContextWrapper");
    //public Object getSystemService(String name)
    jmethodID get_sys_service=env->GetMethodID(context_wrapper_class,"getSystemService",
            "(Ljava/lang/String;)Ljava/lang/Object;");
    AKJavaHookMethodV(env,get_sys_service,
            reinterpret_cast<void*>(my_get_sys_service),&old_method);
    LOGD("old_method:%p\n",old_method);
}